/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 mini_map_renderer.cpp

	$Header: /heroes4/mini_map_renderer.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "mini_map_renderer.h"

#include <assert.h>
#include <map>
#include <utility>

#include "abstract_adv_object.h"
#include "alpha_blend.h"
#include "clip_list.h"
#include "convert_16_bit.h"
#include "counted_ptr.h"
#include "draw_adventure_tile.h"
#include "enum_operations.h"
#include "footprint.h"
#include "pixel_24.h"
#include "player_color.h"
#include "shroud_transition_map.h"
#include "terrain.h"
#include "vector_set.h"

using namespace mini_map_renderer_details;

// ------------------------------------------------------------------------------
// Details namespace
// ------------------------------------------------------------------------------

namespace mini_map_renderer_details
{

	// ------------------------------------------------------------------------------
	// t_internal_map_data class
	// ------------------------------------------------------------------------------

	class t_internal_map_data : public t_counted_object
	{
	public:
		// Types
		typedef t_mini_map_renderer				t_renderer;
		typedef t_renderer::t_adventure_map		t_adventure_map;
		typedef t_renderer::t_object_id			t_object_id;
		typedef t_renderer::t_tile_point_set	t_tile_point_set;

		// Constructor
		explicit t_internal_map_data( t_adventure_map const & map );

		// Member functions
		void	add_renderer( t_renderer * renderer_ptr );
		void	on_adv_object_moved( t_object_id object_id );
		void	on_adv_object_placed( t_object_id object_id );
		void	on_moving_adv_object( t_object_id object_id );
		void	on_removing_adv_object( t_object_id object_id );
		void	on_terrain_changed(
					int							level,
					t_tile_point_set const &	changed_tile_points );
		void	on_visibility_changed(
					int							team_num, 
					int							level, 
					t_tile_point_set const &	changed_tile_points );
		void	refresh();
		int		remove_renderer( t_renderer * renderer_ptr );

	private:
		// Types
		typedef t_vector_set< t_renderer * >	t_renderer_ptr_set;

		// Data members
		t_adventure_map const &	m_map;
		t_renderer_ptr_set		m_renderer_ptrs;
	};

	// ------------------------------------------------------------------------------
	// t_internal_map_data members
	// ------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_internal_map_data::t_internal_map_data( t_adventure_map const & map )
		:	m_map( map )
	{
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::add_renderer( t_renderer * renderer_ptr )
	{
		assert( m_renderer_ptrs.find( renderer_ptr ) == m_renderer_ptrs.end() );
		m_renderer_ptrs.insert( renderer_ptr );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	int t_internal_map_data::remove_renderer( t_renderer * renderer_ptr )
	{
		assert( m_renderer_ptrs.find( renderer_ptr ) != m_renderer_ptrs.end() );
		m_renderer_ptrs.erase( renderer_ptr );
		return m_renderer_ptrs.size();
	}

} // Details namespace

// ------------------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------------------

namespace
{

	// ------------------------------------------------------------------------------
	// t_map_data type
	// ------------------------------------------------------------------------------
	typedef t_internal_map_data t_map_data;
	typedef t_counted_ptr< t_map_data > t_map_data_ptr;

	// ------------------------------------------------------------------------------
	// t_map_data_map class
	// ------------------------------------------------------------------------------
	class t_map_data_map
		:	public t_counted_object,
			public std::map< t_map_data::t_adventure_map const *, t_map_data_ptr >
	{
	};
	typedef t_counted_ptr< t_map_data_map > t_map_data_map_ptr;

	// ------------------------------------------------------------------------------
	// Non-member functions
	// ------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline t_map_data_map_ptr get_map_data_map_ptr()
	{
		static t_map_data_map_ptr map_data_map_ptr( new t_map_data_map );
		return map_data_map_ptr;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_map_data * get_map_data_ptr( t_mini_map_renderer::t_adventure_map const & map )
	{
		t_map_data_map const & map_data_map = *get_map_data_map_ptr();
		t_map_data_map::const_iterator map_data_iter = map_data_map.find( &map );
		return map_data_iter != map_data_map.end() ? map_data_iter->second.get() : 0;
	}

	// ------------------------------------------------------------------------------
	// t_terrain_color_source_table type
	// ------------------------------------------------------------------------------
	typedef t_pixel_24 const * t_terrain_color_source_table[ k_terrain_count ];

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_terrain_color_source_table const & get_terrain_color_source_table()
	{
		static t_pixel_24 const k_water_color_table[ k_terrain_water_subtype_count ] =
		{
			t_pixel_24(  83, 133, 183 ),
			t_pixel_24(  65, 104, 143 ),
		};

		static t_pixel_24 const k_grass_color_table[ k_terrain_grass_subtype_count ] =
		{
			t_pixel_24(  68,  96,  19 ),
			t_pixel_24(  44, 103,  15 ),
		};

		static t_pixel_24 const k_rough_color_table[ k_terrain_rough_subtype_count ] =
		{
			t_pixel_24( 122, 121,  99 ),
			t_pixel_24( 176, 175, 144 ),
		};

		static t_pixel_24 const k_swamp_color_table[ k_terrain_swamp_subtype_count ] =
		{
			t_pixel_24(  62,  65,  37 ),
			t_pixel_24( 107, 112,  64 ),
		};

		static t_pixel_24 const k_lava_color_table[ k_terrain_lava_subtype_count ] =
		{
			t_pixel_24( 131,  26,  13 ),
			t_pixel_24(  53,  33,  47 ),
		};

		static t_pixel_24 const k_snow_color_table[ k_terrain_snow_subtype_count ] =
		{
			t_pixel_24( 233, 236, 245 ),
			t_pixel_24( 190, 203, 241 ),
		};

		static t_pixel_24 const k_sand_color_table[ k_terrain_sand_subtype_count ] =
		{
			t_pixel_24( 222, 210, 145 ),
			t_pixel_24( 252, 206,  96 ),
		};

		static t_pixel_24 const k_dirt_color_table[ k_terrain_dirt_subtype_count ] =
		{
			t_pixel_24( 112,  88,  64 ),
			t_pixel_24(  88,  76,  48 ),
		};

		static t_pixel_24 const k_subterranean_color_table[ k_terrain_subterranean_subtype_count ] =
		{
			t_pixel_24( 165, 156, 107 ),
			t_pixel_24( 216, 209, 182 ),
		};

		static t_pixel_24 const k_water_river_color_table[ k_terrain_water_river_subtype_count ] =
		{
			t_pixel_24(  83, 133, 183 ),
		};

		static t_pixel_24 const k_lava_river_color_table[ k_terrain_lava_river_subtype_count ] =
		{
			t_pixel_24( 223,  47,   0 ),
		};

		static t_pixel_24 const k_ice_river_color_table[ k_terrain_ice_river_subtype_count ] =
		{
			t_pixel_24( 190, 203, 241 ),
		};

		static t_pixel_24 const k_magic_all_color_table[ k_terrain_magic_all_subtype_count ] =
		{
			t_pixel_24( 105, 107, 121 ),
		};

		static t_pixel_24 const k_magic_life_color_table[ k_terrain_magic_life_subtype_count ] =
		{
			t_pixel_24( 186, 127, 211 ),
		};

		static t_pixel_24 const k_magic_order_color_table[ k_terrain_magic_order_subtype_count ] =
		{
			t_pixel_24( 104,  83, 211 ),
		};

		static t_pixel_24 const k_magic_death_color_table[ k_terrain_magic_death_subtype_count ] =
		{
			t_pixel_24(  31,  30,  45 ),
		};

		static t_pixel_24 const k_magic_chaos_color_table[ k_terrain_magic_chaos_subtype_count ] =
		{
			t_pixel_24( 155,  38,  38 ),
		};

		static t_pixel_24 const k_magic_nature_color_table[ k_terrain_magic_nature_subtype_count ] =
		{
			t_pixel_24(  62, 197,  80 ),
		};

		static t_pixel_24 const k_might_color_table[ k_terrain_might_subtype_count ] =
		{
			t_pixel_24( 144,  64,  15 ),
		};

		static t_terrain_color_source_table const k_color_table =
		{
			k_water_color_table,
			k_grass_color_table,
			k_rough_color_table,
			k_swamp_color_table,
			k_lava_color_table,
			k_snow_color_table,
			k_sand_color_table,
			k_dirt_color_table,
			k_subterranean_color_table,
			k_water_river_color_table,
			k_lava_river_color_table,
			k_ice_river_color_table,
			k_magic_all_color_table,
			k_magic_life_color_table,
			k_magic_order_color_table,
			k_magic_death_color_table,
			k_magic_chaos_color_table,
			k_magic_nature_color_table,
			k_might_color_table,
		};

		return k_color_table;
	}

	// ------------------------------------------------------------------------------
	// t_terrain_color_table type
	// ------------------------------------------------------------------------------
	typedef t_uint16 * t_terrain_color_table[ k_terrain_count ];

	// ------------------------------------------------------------------------------
	// t_terrain_color_table_maintainer class
	// ------------------------------------------------------------------------------

	class t_terrain_color_table_maintainer : private t_pixel_mask_viewer
	{
	public:
		// Constructor
		explicit t_terrain_color_table_maintainer( t_terrain_color_table & table );

	private:
		// Data members
		t_terrain_color_table &	m_table;

		// Member functions
		void			build_color_table();
		virtual void	on_pixel_masks_changed();
	};

	// ------------------------------------------------------------------------------
	// t_terrain_color_table_maintainer members
	// ------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_terrain_color_table_maintainer::t_terrain_color_table_maintainer(
		t_terrain_color_table &	table )
		:	m_table( table )
	{
		build_color_table();
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_terrain_color_table_maintainer::build_color_table()
	{
		static t_terrain_color_source_table const & k_source_table =
			get_terrain_color_source_table();

		t_terrain_type type;
		for ( type = t_terrain_type( 0 ); type < k_terrain_count; enum_incr( type ) )
		{
			int subtype_count = get_terrain_subtype_count( type );
			int subtype;
			for ( subtype = 0; subtype < subtype_count; ++subtype )
				m_table[ type ][ subtype ] = convert_to_16_bit( k_source_table[ type ][ subtype ] );
		}
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_terrain_color_table_maintainer::on_pixel_masks_changed()
	{
		build_color_table();
	}

	// ------------------------------------------------------------------------------
	// t_player_color_source_table type
	// ------------------------------------------------------------------------------
	typedef t_pixel_24 t_player_color_source_table[ k_player_color_count ];

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_player_color_source_table const & get_player_color_source_table()
	{
		static t_player_color_source_table const k_color_table =
		{
			t_pixel_24( 230,  60,  60 ), // red
			t_pixel_24(  22,  51, 224 ), // blue
			t_pixel_24(  20, 160,  20 ), // green
			t_pixel_24( 248, 157,  13 ), // orange
			t_pixel_24( 210,  70, 210 ), // purple
			t_pixel_24(  70, 220, 220 ), // teal
			t_pixel_24( 160, 160, 160 ), // gray
		};
		return k_color_table;
	};

	// ------------------------------------------------------------------------------
	// t_player_color_table type
	// ------------------------------------------------------------------------------
	typedef t_uint16 t_player_color_table[ k_player_color_count ];

	// ------------------------------------------------------------------------------
	// t_player_color_table_maintainer class
	// ------------------------------------------------------------------------------

	class t_player_color_table_maintainer : private t_pixel_mask_viewer
	{
	public:
		// Constructor
		explicit t_player_color_table_maintainer( t_player_color_table & color_table );

	private:
		// Data members
		t_player_color_table &	m_color_table;

		// Member functions
		void			build_color_table();
		virtual void	on_pixel_masks_changed();
	};

	// ------------------------------------------------------------------------------
	// t_player_color_table_maintainer class
	// ------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_player_color_table_maintainer::t_player_color_table_maintainer(
		t_player_color_table &	color_table )
		:	m_color_table( color_table )
	{
		build_color_table();
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_player_color_table_maintainer::build_color_table()
	{
		static t_player_color_source_table const & color_source_table =
			get_player_color_source_table();

		t_player_color color;
		for ( color = t_player_color( 0 ); color < k_player_color_count; enum_incr( color ) )
			m_color_table[ color ] = convert_to_16_bit( color_source_table[ color ] );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_player_color_table_maintainer::on_pixel_masks_changed()
	{
		build_color_table();
	}

	// ------------------------------------------------------------------------------
	// Non-member functions
	// ------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline t_uint16 get_terrain_color( t_terrain_type type, int subtype )
	{
		assert( type >= 0 && type < k_terrain_count );
		assert( subtype >= 0 && subtype < get_terrain_subtype_count( type ) );

		static t_uint16 water_color_table[ k_terrain_water_subtype_count ];
		static t_uint16 grass_color_table[ k_terrain_grass_subtype_count ];
		static t_uint16 rough_color_table[ k_terrain_rough_subtype_count ];
		static t_uint16 swamp_color_table[ k_terrain_swamp_subtype_count ];
		static t_uint16 lava_color_table[ k_terrain_lava_subtype_count ];
		static t_uint16 snow_color_table[ k_terrain_snow_subtype_count ];
		static t_uint16 sand_color_table[ k_terrain_sand_subtype_count ];
		static t_uint16 dirt_color_table[ k_terrain_dirt_subtype_count ];
		static t_uint16 subterranean_color_table[ k_terrain_subterranean_subtype_count ];
		static t_uint16 water_river_color_table[ k_terrain_water_river_subtype_count ];
		static t_uint16 lava_river_color_table[ k_terrain_lava_river_subtype_count ];
		static t_uint16 ice_river_color_table[ k_terrain_ice_river_subtype_count ];
		static t_uint16 magic_all_color_table[ k_terrain_magic_all_subtype_count ];
		static t_uint16 magic_life_color_table[ k_terrain_magic_life_subtype_count ];
		static t_uint16 magic_order_color_table[ k_terrain_magic_order_subtype_count ];
		static t_uint16 magic_death_color_table[ k_terrain_magic_death_subtype_count ];
		static t_uint16 magic_chaos_color_table[ k_terrain_magic_chaos_subtype_count ];
		static t_uint16 magic_nature_color_table[ k_terrain_magic_nature_subtype_count ];
		static t_uint16 might_color_table[ k_terrain_might_subtype_count ];

		static t_terrain_color_table color_table =
		{
			water_color_table,
			grass_color_table,
			rough_color_table,
			swamp_color_table,
			lava_color_table,
			snow_color_table,
			sand_color_table,
			dirt_color_table,
			subterranean_color_table,
			water_river_color_table,
			lava_river_color_table,
			ice_river_color_table,
			magic_all_color_table,
			magic_life_color_table,
			magic_order_color_table,
			magic_death_color_table,
			magic_chaos_color_table,
			magic_nature_color_table,
			might_color_table,
		};

		static t_terrain_color_table_maintainer const k_maintainer( color_table );

		return color_table[ type ][ subtype ];
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline t_uint16 get_player_color( t_player_color color )
	{
		assert( color >= 0 && color < k_player_color_count );

		static t_player_color_table color_table;
		static t_player_color_table_maintainer const k_maintainer( color_table );

		return color_table[ color ];
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_uint16 get_terrain_color(
		t_abstract_adventure_map const &	map,
		t_map_point_2d const &				tile_point,
		t_abstract_adventure_tile const &	tile,
		int									team_view )
	{
		bool darken = false;

		int object_count = tile.get_intersecting_object_count();
		int object_num;
		for ( object_num = 0; object_num < object_count; ++object_num )
		{
			t_abstract_adventure_map::t_object_id object_id =
				tile.get_intersecting_object_id( object_num );

			t_abstract_adv_object const & object = map.get_object( object_id );

			if ( team_view >= 0 && !object.is_visible_to( team_view ) )
				continue;

			t_player_color color = object.get_player_color();
			if ( color != k_player_none )
				return get_player_color( color );

			if ( !darken && object.is_decorative() )
			{
				t_map_point_2d const & object_pos = map.get_object_pos( object_id );
				if ( object.get_footprint().is_cell_impassable( tile_point - object_pos ) )
					darken = true;
			}
		}

		t_uint16 result = get_terrain_color( tile.get_terrain(), tile.get_terrain_subtype() );
		if ( darken )
			result -= alpha_4( result );
		return result;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline t_uint16 get_lit_terrain_color(
		t_abstract_adventure_map const &	map,
		t_level_map_point_2d const &		tile_point,
		t_abstract_adventure_tile const &	tile,
		int									team_view,
		bool								x_odd,
		bool								y_odd )
	{
		t_uint16 result;
		if ( tile.get_rock_terrain_type() == k_rock_terrain_none )
		{
			result = get_terrain_color( map, tile_point, tile, team_view );
			result = apply_lighting(
						result,
						map.get_tile_vertex( tile_point ).get_light(),
						x_odd,
						y_odd );
		}
		else
			result = 0;

		return result;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void draw_rect(
		t_screen_rect const &	rect,
		t_abstract_bitmap16 &	dest,
		t_uint16				color,
		t_screen_rect const &	clip_rect )
	{
		if ( !is_normalized( rect ) )
			return;

		if ( rect.width() <= 2 || rect.height() <= 2 )
		{
			if ( intersect( rect, clip_rect ) )
				fill( dest, intersection( rect, clip_rect ), color );
			return;
		}

		t_screen_rect edge_rect( rect.left, rect.top, rect.right, rect.top + 1 );
		if ( intersect( edge_rect, clip_rect ) )
			fill( dest, intersection( edge_rect, clip_rect ), color );

		edge_rect.top = rect.bottom - 1;
		edge_rect.bottom = rect.bottom;
		if ( intersect( edge_rect, clip_rect ) )
			fill( dest, intersection( edge_rect, clip_rect ), color );

		edge_rect.top = rect.top + 1;
		edge_rect.bottom = rect.bottom - 1;
		edge_rect.left = rect.left;
		edge_rect.right = rect.left + 1;
		if ( intersect( edge_rect, clip_rect ) )
			fill( dest, intersection( edge_rect, clip_rect ), color );

		edge_rect.left = rect.right - 1;
		edge_rect.right = rect.right;
		if ( intersect( edge_rect, clip_rect ) )
			fill( dest, intersection( edge_rect, clip_rect ), color );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	bool transitions_to_unexplored(
		t_shroud_transition_map const &	transition_map,
		t_level_map_point_2d const &	point )
	{
		typedef t_shroud_transition_map::t_transition_list t_transition_list;
		t_transition_list const & transitions = transition_map.get_transitions( point );

		t_transition_list::const_iterator transition_end = transitions.end();
		t_transition_list::const_iterator transition_iter = transitions.begin();
		for ( ; transition_iter != transition_end; ++transition_iter )
		{
			if ( transition_iter->get_visibility() == k_tile_never_explored )
				return true;
		}
		return false;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_uint16 get_color(
		t_abstract_adventure_map const &	map,
		t_shroud_transition_map const *		shroud_transition_map_ptr,
		int									team_view,
		t_level_map_point_2d const &		tile_point,
		bool								x_odd,
		bool								y_odd )
	{
		typedef t_abstract_adventure_map::t_tile t_tile;

		t_tile const & tile = map.get_tile( tile_point );

		t_uint16 result;
		if ( team_view < 0 || tile.get_visibility( team_view ) != k_tile_never_explored )
		{
			result = get_lit_terrain_color( map, tile_point, tile, team_view, x_odd, y_odd );
			if (	shroud_transition_map_ptr != 0
				&&	transitions_to_unexplored( *shroud_transition_map_ptr, tile_point ) )
				result = alpha_blend16( result, 0, 5 );
		}
		else
		{
			result = 0;
			if (	shroud_transition_map_ptr != 0
				&&	!shroud_transition_map_ptr->get_transitions( tile_point ).empty() )
				result = alpha_blend16( result, get_lit_terrain_color( map, tile_point, tile, team_view, x_odd, y_odd ), 5 );
		}

		return result;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void invalidate_viewport_rect(
		t_screen_rect			rect,
		t_screen_rect const &	view_rect,
		t_clip_list &			clip_list )
	{
		--rect.left;
		++rect.right;
		--rect.top;
		++rect.bottom;

		if ( rect.width() <= 10 || rect.height() <= 10 )
		{
			if ( intersect( rect, view_rect ) )
				clip_list += intersection( rect, view_rect );
		}
		else
		{
			t_screen_rect edge_rect( rect.left, rect.top, rect.right, rect.top + 2 );
			if ( intersect( edge_rect, view_rect ) )
				clip_list += intersection( edge_rect, view_rect );

			edge_rect.top = rect.bottom - 2;
			edge_rect.bottom = rect.bottom;
			if ( intersect( edge_rect, view_rect ) )
				clip_list += intersection( edge_rect, view_rect );

			edge_rect.left = rect.left;
			edge_rect.right = rect.left + 2;
			edge_rect.top = rect.top + 2;
			edge_rect.bottom = rect.bottom - 2;
			if ( intersect( edge_rect, view_rect ) )
				clip_list += intersection( edge_rect, view_rect );

			edge_rect.left = rect.right - 2;
			edge_rect.right = rect.right;
			if ( intersect( edge_rect, view_rect ) )
				clip_list += intersection( edge_rect, view_rect );
		}
	}

} // Unnamed namespace

// ------------------------------------------------------------------------------
// t_mini_map_renderer_client members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer_client::on_rect_dirtied( t_screen_rect const & dirtied_rect )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer_client::on_view_level_changed( int level )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer_client::on_view_resized( t_screen_point const & size )
{
}

// ------------------------------------------------------------------------------
// t_mini_map_renderer::t_impl class
// ------------------------------------------------------------------------------

class t_mini_map_renderer::t_impl
{
public:
	// Types
	typedef t_mini_map_renderer::t_client			t_client;
	typedef t_mini_map_renderer::t_back_buffer_ptr	t_back_buffer_ptr;
	typedef t_mini_map_renderer::t_adventure_map	t_adventure_map;
	typedef t_mini_map_renderer::t_object			t_object;
	typedef t_mini_map_renderer::t_object_id		t_object_id;
	typedef t_mini_map_renderer::t_tile_point_set	t_tile_point_set;

	// Data members
	t_mini_map_renderer &		m_outer;

	t_client *					m_client_ptr;
	t_adventure_map const &		m_adventure_map;

	t_map_data_map_ptr			m_map_data_map_ptr;
	t_map_data_map::iterator	m_map_data_iter;

	t_screen_rect				m_viewport_rect;
	
	t_screen_point				m_view_size;
	int							m_view_level;
	int							m_team_view;
	t_back_buffer_ptr			m_back_buffer_ptr;

	t_clip_list					m_dirty_list;

	// Constructors
	t_impl(
		t_mini_map_renderer &	outer,
		t_client *				client_ptr,
		t_adventure_map const &	adventure_map,
		int						team_view );
	~t_impl();

	// Member functions
	void			dirty( t_screen_rect const & rect );
	void			dirty( t_clip_list const & clip_list );
	void			draw( t_screen_rect const & rect );
	t_screen_rect	get_object_extent( t_object_id object_id ) const;
	void			invalidate_object( t_object_id object_id );
	void			on_adv_object_moved( t_object_id object_id );
	void			on_adv_object_placed( t_object_id object_id );
	void			on_moving_adv_object( t_object_id object_id );
	void			on_removing_adv_object( t_object_id object_id );
	void			on_terrain_changed(
						int							level,
						t_tile_point_set const &	changed_tile_points );
	void			on_visibility_changed(
						int							team_num, 
						int							level, 
						t_tile_point_set const &	changed_tile_points );
	void			refresh();
};

// ------------------------------------------------------------------------------
// t_mini_map_renderer::t_impl members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_mini_map_renderer::t_impl::t_impl(
	t_mini_map_renderer &	outer,
	t_client *				client_ptr,
	t_adventure_map const &	adventure_map,
	int						team_view )
	:	m_outer( outer ),
		m_client_ptr( client_ptr ),
		m_adventure_map( adventure_map ),
		m_map_data_map_ptr( get_map_data_map_ptr() ),
		m_view_level( 0 ),
		m_viewport_rect( 0, 0, 0, 0 ),
		m_team_view( team_view )
{
	// Look up the map data for the specified map
	m_map_data_iter = m_map_data_map_ptr->find( &m_adventure_map );

	if ( m_map_data_iter == m_map_data_map_ptr->end() )
	{
		// Didn't find map data for this map so add it
		t_map_data_ptr new_map_data_ptr( new t_map_data( m_adventure_map ) );
		new_map_data_ptr->add_renderer( &m_outer );
		m_map_data_iter = m_map_data_map_ptr->insert( std::make_pair( &m_adventure_map, new_map_data_ptr ) ).first;
	}
	else
		m_map_data_iter->second->add_renderer( &m_outer );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_mini_map_renderer::t_impl::~t_impl()
{
	// Remove ourself for the map data
	if ( m_map_data_iter->second->remove_renderer( &m_outer ) == 0 )
		m_map_data_map_ptr->erase( m_map_data_iter );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::dirty( t_screen_rect const & rect )
{
	m_dirty_list += rect;
	m_outer.on_rect_dirtied( rect );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::dirty( t_clip_list const & clip_list )
{
	m_dirty_list += clip_list;

	t_clip_list::t_const_iterator rect_end = clip_list.end();
	t_clip_list::t_const_iterator rect_iter = clip_list.begin();
	for ( ; rect_iter != rect_end; ++rect_iter )
		m_outer.on_rect_dirtied( *rect_iter );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::draw( t_screen_rect const & rect )
{
	if ( m_back_buffer_ptr.get() == 0 )
		return;

	if ( m_team_view != k_player_gray )
	{
		// Get the shroud transition map
		t_shroud_transition_map const * shroud_transition_map_ptr;
		if ( m_team_view >= 0 )
			shroud_transition_map_ptr = m_adventure_map.get_shroud_transition_map_ptr( m_team_view );
		else
			shroud_transition_map_ptr = 0;

		int map_size = m_adventure_map.get_size();

		int view_width_times_height = m_view_size.x * m_view_size.y;
		int map_size_times_width = map_size * m_view_size.x;
		int map_size_times_height = map_size * m_view_size.y;

		t_uint16 * dest_line_ptr = m_back_buffer_ptr->advance_line( m_back_buffer_ptr->get_data_ptr(), rect.top ) + rect.left;

		t_map_point_2d row_start;
		row_start.row = ( rect.top * m_view_size.x - rect.left * m_view_size.y + view_width_times_height ) * map_size;
		row_start.column = ( rect.top * m_view_size.x + rect.left * m_view_size.y ) * map_size;

		t_screen_point screen_point;
		for ( screen_point.y = rect.top; screen_point.y < rect.bottom; ++screen_point.y )
		{
			t_map_point_2d point = row_start;
			t_uint16 * dest_ptr = dest_line_ptr;

			for ( screen_point.x = rect.left; screen_point.x < rect.right; ++screen_point.x )
			{
				t_level_map_point_2d tile_point( point / ( 2 * view_width_times_height ), m_view_level );
				*dest_ptr =
					get_color(
						m_adventure_map,
						shroud_transition_map_ptr,
						m_team_view,
						tile_point,
						( screen_point.x & 1 ) != 0,
						( screen_point.y & 1 ) != 0 );

				++dest_ptr;
				point.column += map_size_times_height;
				point.row -= map_size_times_height;						
			}

			row_start.column += map_size_times_width;
			row_start.row += map_size_times_width;
			dest_line_ptr = m_back_buffer_ptr->advance_line( dest_line_ptr );
		}

		// Draw the viewport
		t_screen_rect viewport_rect = m_outer.view_to_mini( m_viewport_rect );
		--viewport_rect.left;
		++viewport_rect.right;
		--viewport_rect.top;
		++viewport_rect.bottom;

		if ( intersect( viewport_rect, rect ) )
		{
			t_uint16 const k_viewport_color_dark = convert_to_16_bit( 0, 0, 0 );
			t_uint16 const k_viewport_color_light = convert_to_16_bit( 255, 255, 255 );

			draw_rect( viewport_rect, *m_back_buffer_ptr, k_viewport_color_dark, rect );

			++viewport_rect.left;
			--viewport_rect.right;
			++viewport_rect.top;
			--viewport_rect.bottom;

			draw_rect( viewport_rect, *m_back_buffer_ptr, k_viewport_color_light, rect );
		}
	}
	else
		fill( *m_back_buffer_ptr, rect, 0 );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_rect t_mini_map_renderer::t_impl::get_object_extent(
	t_object_id object_id ) const
{
	int map_size = m_adventure_map.get_size();
	t_object const & object = m_adventure_map.get_object( object_id );

	t_map_point_2d const & object_size = object.get_footprint().get_size();
	t_map_point_2d const & top_pos = m_adventure_map.get_object_pos( object_id );
	t_map_point_2d left_pos( top_pos.row + object_size.row, top_pos.column );
	t_map_point_2d right_pos( top_pos.row, top_pos.column + object_size.column );
	t_map_point_2d bottom_pos = top_pos + object_size;

	return	t_screen_rect(
				m_view_size.x * ( map_size + 2 * ( left_pos.column - left_pos.row ) ) / ( 2 * map_size ),
				m_view_size.y * ( map_size + 2 * ( top_pos.column + top_pos.row - map_size ) ) / ( 2 * map_size ),
				( m_view_size.x * ( map_size + 2 * ( right_pos.column  - right_pos.row ) ) + 2 * map_size - 1 ) / ( 2 * map_size ),
				( m_view_size.y * ( map_size + 2 * ( bottom_pos.column + bottom_pos.row - map_size ) ) + 2 * map_size - 1 ) / ( 2 * map_size ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::invalidate_object( t_object_id object_id )
{
	t_level_map_point_2d const & object_pos = m_adventure_map.get_object_pos( object_id );
	if ( object_pos.level != m_view_level )
		return;

	t_screen_rect view_rect( t_screen_point( 0, 0 ), m_view_size );
	t_screen_rect object_extent = get_object_extent( object_id );
	if ( intersect( object_extent, view_rect ) )
		dirty( intersection( object_extent, view_rect ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::on_adv_object_moved( t_object_id object_id )
{
	invalidate_object( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::on_adv_object_placed( t_object_id object_id )
{
	invalidate_object( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::on_moving_adv_object( t_object_id object_id )
{
	invalidate_object( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::on_removing_adv_object( t_object_id object_id )
{
	invalidate_object( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::on_terrain_changed(
	int							level,
	t_tile_point_set const &	changed_tile_points )
{
	assert( level >= 0 && level < m_adventure_map.get_num_levels() );

	t_tile_point_set::const_iterator tile_point_iter = changed_tile_points.begin();
	if ( tile_point_iter != changed_tile_points.end() )
	{
		t_screen_rect update_rect = m_outer.get_tile_rect( *tile_point_iter );
		for ( ++tile_point_iter; tile_point_iter != changed_tile_points.end(); ++tile_point_iter )
			update_rect = get_extent( update_rect, m_outer.get_tile_rect( *tile_point_iter ) );

		t_screen_rect view_rect( t_screen_point( 0, 0 ), m_view_size );
		if ( intersect( update_rect, view_rect ) )
			dirty( intersection( update_rect, view_rect ) );
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::on_visibility_changed(
	int							team_num, 
	int							level, 
	t_tile_point_set const &	changed_tile_points )
{
	if ( team_num == m_team_view )
		on_terrain_changed( level, changed_tile_points );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::t_impl::refresh()
{
	dirty( t_screen_rect( 0, 0, m_view_size.x, m_view_size.y ) );
}

// ------------------------------------------------------------------------------
// Details namespace
// ------------------------------------------------------------------------------

namespace mini_map_renderer_details
{

	// ------------------------------------------------------------------------------
	// t_internal_map_data members
	// ------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::on_adv_object_moved( t_object_id object_id )
	{
		t_renderer_ptr_set::const_iterator renderer_ptr_end = m_renderer_ptrs.end();
		t_renderer_ptr_set::const_iterator renderer_ptr_iter = m_renderer_ptrs.begin();
		for ( ; renderer_ptr_iter != renderer_ptr_end; ++renderer_ptr_iter )
			( *renderer_ptr_iter )->m_impl_ptr->on_adv_object_moved( object_id );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::on_adv_object_placed( t_object_id object_id )
	{
		t_renderer_ptr_set::const_iterator renderer_ptr_end = m_renderer_ptrs.end();
		t_renderer_ptr_set::const_iterator renderer_ptr_iter = m_renderer_ptrs.begin();
		for ( ; renderer_ptr_iter != renderer_ptr_end; ++renderer_ptr_iter )
			( *renderer_ptr_iter )->m_impl_ptr->on_adv_object_placed( object_id );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::on_moving_adv_object( t_object_id object_id )
	{
		t_renderer_ptr_set::const_iterator renderer_ptr_end = m_renderer_ptrs.end();
		t_renderer_ptr_set::const_iterator renderer_ptr_iter = m_renderer_ptrs.begin();
		for ( ; renderer_ptr_iter != renderer_ptr_end; ++renderer_ptr_iter )
			( *renderer_ptr_iter )->m_impl_ptr->on_moving_adv_object( object_id );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::on_removing_adv_object( t_object_id object_id )
	{
		t_renderer_ptr_set::const_iterator renderer_ptr_end = m_renderer_ptrs.end();
		t_renderer_ptr_set::const_iterator renderer_ptr_iter = m_renderer_ptrs.begin();
		for ( ; renderer_ptr_iter != renderer_ptr_end; ++renderer_ptr_iter )
			( *renderer_ptr_iter )->m_impl_ptr->on_removing_adv_object( object_id );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::on_terrain_changed(
		int							level,
		t_tile_point_set const &	changed_tile_points )
	{
		t_renderer_ptr_set::const_iterator renderer_ptr_end = m_renderer_ptrs.end();
		t_renderer_ptr_set::const_iterator renderer_ptr_iter = m_renderer_ptrs.begin();
		for ( ; renderer_ptr_iter != renderer_ptr_end; ++renderer_ptr_iter )
			( *renderer_ptr_iter )->m_impl_ptr->on_terrain_changed( level, changed_tile_points );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::on_visibility_changed(
		int							team_num, 
		int							level, 
		t_tile_point_set const &	changed_tile_points )
	{
		t_renderer_ptr_set::const_iterator renderer_ptr_end = m_renderer_ptrs.end();
		t_renderer_ptr_set::const_iterator renderer_ptr_iter = m_renderer_ptrs.begin();
		for ( ; renderer_ptr_iter != renderer_ptr_end; ++renderer_ptr_iter )
			( *renderer_ptr_iter )->m_impl_ptr->on_visibility_changed( team_num, level, changed_tile_points );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_internal_map_data::refresh()
	{
		t_renderer_ptr_set::const_iterator renderer_ptr_end = m_renderer_ptrs.end();
		t_renderer_ptr_set::const_iterator renderer_ptr_iter = m_renderer_ptrs.begin();
		for ( ; renderer_ptr_iter != renderer_ptr_end; ++renderer_ptr_iter )
			( *renderer_ptr_iter )->m_impl_ptr->refresh();
	}

} // Details namespace

// ------------------------------------------------------------------------------
// t_mini_map_renderer members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_adv_object_moved(
	t_adventure_map const &	adventure_map,
	t_object_id				object_id )
{
	t_map_data * map_data_ptr = get_map_data_ptr( adventure_map );
	if ( map_data_ptr != 0 )
		map_data_ptr->on_adv_object_moved( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_adv_object_placed(
	t_adventure_map const &	adventure_map,
	t_object_id				object_id )
{
	t_map_data * map_data_ptr = get_map_data_ptr( adventure_map );
	if ( map_data_ptr != 0 )
		map_data_ptr->on_adv_object_placed( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_moving_adv_object(
	t_adventure_map const &	adventure_map,
	t_object_id				object_id )
{
	t_map_data * map_data_ptr = get_map_data_ptr( adventure_map );
	if ( map_data_ptr != 0 )
		map_data_ptr->on_moving_adv_object( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_removing_adv_object(
	t_adventure_map const &	adventure_map,
	t_object_id				object_id )
{
	t_map_data * map_data_ptr = get_map_data_ptr( adventure_map );
	if ( map_data_ptr != 0 )
		map_data_ptr->on_removing_adv_object( object_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_terrain_changed(
	t_adventure_map const &		adventure_map,
	int							level,
	t_tile_point_set const &	changed_tile_points )
{
	t_map_data * map_data_ptr = get_map_data_ptr( adventure_map );
	if ( map_data_ptr != 0 )
		map_data_ptr->on_terrain_changed( level, changed_tile_points );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_visibility_changed(
	t_adventure_map const &		adventure_map,
	int							team_num, 
	int							level, 
	t_tile_point_set const &	changed_tile_points )
{
	t_map_data * map_data_ptr = get_map_data_ptr( adventure_map );
	if ( map_data_ptr != 0 )
		map_data_ptr->on_visibility_changed( team_num, level, changed_tile_points );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::refresh( t_adventure_map const & adventure_map )
{
	t_map_data * map_data_ptr = get_map_data_ptr( adventure_map );
	if ( map_data_ptr != 0 )
		map_data_ptr->refresh();
}

#pragma warning( push )
#pragma warning( disable: 4355 )
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_mini_map_renderer::t_mini_map_renderer(
	t_adventure_map const &	adventure_map,
	int						team_view)
	:	m_impl_ptr( new t_impl( *this, 0, adventure_map, team_view ) )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_mini_map_renderer::t_mini_map_renderer(
	t_client &				client,
	t_adventure_map const &	adventure_map,
	int						team_view )
	:	m_impl_ptr( new t_impl( *this, &client, adventure_map, team_view ) )
{
}
#pragma warning( pop )

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_mini_map_renderer::~t_mini_map_renderer()
{	
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_mini_map_renderer::t_adventure_map const &
t_mini_map_renderer::get_adventure_map() const
{
	return m_impl_ptr->m_adventure_map;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
int t_mini_map_renderer::get_view_level() const
{
	return m_impl_ptr->m_view_level;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_point t_mini_map_renderer::get_view_size() const
{
	return m_impl_ptr->m_view_size;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_rect const & t_mini_map_renderer::get_viewport_rect() const
{
	return m_impl_ptr->m_viewport_rect;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::resize( t_screen_point const & new_size )
{
	assert( new_size.x >= 0 && new_size.y >= 0 );

	t_impl & impl = *m_impl_ptr;

	if ( new_size == impl.m_view_size )
		return;

	impl.m_view_size = new_size;

	on_view_resized( impl.m_view_size );

	if ( impl.m_view_size.x > 0 && impl.m_view_size.y > 0 )
	{
		impl.m_back_buffer_ptr = create_back_buffer( impl.m_view_size );
		assert(		impl.m_back_buffer_ptr->get_width() >= impl.m_view_size.x
				&&	impl.m_back_buffer_ptr->get_height() >= impl.m_view_size.y );

		impl.dirty( t_screen_rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y ) );
	}
	else
		impl.m_back_buffer_ptr = 0;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::set_team_view( int new_team_view )
{
	t_impl & impl = *m_impl_ptr;
	if ( new_team_view != impl.m_team_view )
	{
		impl.m_team_view = new_team_view;
		impl.refresh();
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::set_view_level( int new_view_level )
{
	t_impl & impl = *m_impl_ptr;

	assert( new_view_level >= 0 && new_view_level < impl.m_adventure_map.get_num_levels() );

	if ( new_view_level == impl.m_view_level )
		return;

	impl.m_view_level = new_view_level;

	on_view_level_changed( impl.m_view_level );

	if ( impl.m_view_size.x > 0 && impl.m_view_size.y > 0 )
		impl.dirty( t_screen_rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::set_viewport_rect(
	t_screen_rect const &	new_viewport_rect )
{
	t_impl & impl = *m_impl_ptr;

	if ( new_viewport_rect == impl.m_viewport_rect )
		return;

	t_screen_rect old_mini_rect = view_to_mini( impl.m_viewport_rect );
	t_screen_rect new_mini_rect = view_to_mini( new_viewport_rect );

	impl.m_viewport_rect = new_viewport_rect;

	if ( new_mini_rect != old_mini_rect )
	{
		t_screen_rect view_rect( t_screen_point( 0, 0 ), impl.m_view_size );

		t_clip_list clip_list;
		invalidate_viewport_rect( old_mini_rect, view_rect, clip_list );
		invalidate_viewport_rect( new_mini_rect, view_rect, clip_list );

		impl.dirty( clip_list );
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::update( t_screen_rect const & rect )
{
	t_impl & impl = *m_impl_ptr;

	t_clip_list const & clip_list = intersection( impl.m_dirty_list, rect );

	t_clip_list::t_const_iterator rect_end = clip_list.end();
	t_clip_list::t_const_iterator rect_iter = clip_list.begin();
	for ( ; rect_iter != rect_end; ++rect_iter )
		impl.draw( *rect_iter );

	impl.m_dirty_list -= rect;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_mini_map_renderer::t_back_buffer_ptr t_mini_map_renderer::create_back_buffer(
	t_screen_point const &	size )
{
	t_client * client_ptr = m_impl_ptr->m_client_ptr;
	assert( client_ptr != 0 );
	return client_ptr->create_back_buffer( size );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_rect_dirtied( t_screen_rect const & dirtied_rect )
{
	t_client * client_ptr = m_impl_ptr->m_client_ptr;
	assert( client_ptr != 0 );
	client_ptr->on_rect_dirtied( dirtied_rect );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_view_level_changed( int level )
{
	t_client * client_ptr = m_impl_ptr->m_client_ptr;
	assert( client_ptr != 0 );
	client_ptr->on_view_level_changed( level );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_mini_map_renderer::on_view_resized( t_screen_point const & size )
{
	t_client * client_ptr = m_impl_ptr->m_client_ptr;
	assert( client_ptr != 0 );
	client_ptr->on_view_resized( size );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_rect t_mini_map_renderer::get_tile_rect(
	t_map_point_2d const & tile_point ) const
{
	t_impl const & impl = *m_impl_ptr;
	int map_size = impl.m_adventure_map.get_size();
	return	t_screen_rect(
				impl.m_view_size.x * ( map_size + 2 * ( tile_point.column - ( tile_point.row + 1 ) ) ) / ( 2 * map_size ),
				impl.m_view_size.y * ( map_size + 2 * ( tile_point.column + tile_point.row - map_size ) ) / ( 2 * map_size ),
				( impl.m_view_size.x * ( map_size + 2 * ( ( tile_point.column + 1 ) - tile_point.row ) ) + 2 * map_size - 1 ) / ( 2 * map_size ),
				( impl.m_view_size.y * ( map_size + 2 * ( tile_point.column + tile_point.row + 2 - map_size ) ) + 2 * map_size - 1 ) / ( 2 * map_size ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_point t_mini_map_renderer::view_to_mini(
	t_screen_point const &	point ) const
{
	t_impl const & impl = *m_impl_ptr;
	t_screen_point view_size = impl.m_adventure_map.get_view_size();
	return t_screen_point( point.x * impl.m_view_size.x / view_size.x, point.y * impl.m_view_size.y / view_size.y );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_rect t_mini_map_renderer::view_to_mini(
	t_screen_rect const &	rect ) const
{
	t_impl const & impl = *m_impl_ptr;
	t_screen_point view_size = impl.m_adventure_map.get_view_size();
	return	t_screen_rect(
				rect.left * impl.m_view_size.x / view_size.x,
				rect.top * impl.m_view_size.y / view_size.y,
				rect.right * impl.m_view_size.x / view_size.x,
				rect.bottom * impl.m_view_size.y / view_size.y );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_point t_mini_map_renderer::mini_to_view( t_screen_point const & point ) const
{
	t_impl const & impl = *m_impl_ptr;
	t_screen_point view_size = impl.m_adventure_map.get_view_size();
	return t_screen_point( point.x * view_size.x / impl.m_view_size.x, point.y * view_size.y / impl.m_view_size.y );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_rect t_mini_map_renderer::mini_to_view(
	t_screen_rect const &	rect ) const
{
	t_impl const & impl = *m_impl_ptr;
	t_screen_point view_size = impl.m_adventure_map.get_view_size();
	return	t_screen_rect(
				rect.left * view_size.x / impl.m_view_size.x,
				rect.top * view_size.y / impl.m_view_size.y,
				rect.right * view_size.x / impl.m_view_size.x,
				rect.bottom * view_size.y / impl.m_view_size.y );
}
